//*******************************************************
// **** Routines For Fat32 IMPLEMATATION OF SD CARD ****
//**********************************************************
//Controller: ATmega32 (Clock: 8 Mhz-internal)
//Compiler	: AVR-GCC (winAVR with AVRStudio)
//Version 	: 2.3
//Author	: CC Dharmani, Chennai (India)
//			  www.dharmanitech.com
//Date		: 08 May 2010
//********************************************************

//Link to the Post: http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html

// --------------------------------------------------------
// Modified by Rodrigo Garcia , 2016
// --------------------------------------------------------

//**************************************************
// ***** SOURCE FILE : FAT32.c ******
//**************************************************

#include <avr/io.h>
#include <avr/pgmspace.h>
#include "FAT32.h"
#include "SD_routines.h"

#ifdef VERBOSE_FAT32
#include "usartio.h"
#endif

//***************************************************************************
//Function: to read data from boot sector of SD card, to determine important
//parameters like bytesPerSector, sectorsPerCluster etc.
//Arguments: none
//return: none
//***************************************************************************
unsigned char getBootSectorData (void)
{
  struct BS_Structure *bpb; //mapping the buffer onto the structure
  struct MBRinfo_Structure *mbr;
  struct partitionInfo_Structure *partition;
  unsigned long dataSectors;

  unusedSectors = 0;

  SD_readSingleBlock(0);
  bpb = (struct BS_Structure *)buffer;

  if(bpb->jumpBoot[0]!=0xE9 && bpb->jumpBoot[0]!=0xEB)   //check if it is boot sector
    {
      mbr = (struct MBRinfo_Structure *) buffer;       //if it is not boot sector, it must be MBR
  
      if(mbr->signature != 0xaa55) return 1;       //if it is not even MBR then it's not FAT32
  	
      partition = (struct partitionInfo_Structure *)(mbr->partitionData);//first partition
      unusedSectors = partition->firstSector; //the unused sectors, hidden to the FAT
  
      SD_readSingleBlock(partition->firstSector);//read the bpb sector
      bpb = (struct BS_Structure *)buffer;
      if(bpb->jumpBoot[0]!=0xE9 && bpb->jumpBoot[0]!=0xEB) return 1; 
    }

  bytesPerSector = bpb->bytesPerSector;
  //transmitHex(INT, bytesPerSector); transmitByte(' ');
  sectorPerCluster = bpb->sectorPerCluster;
  //transmitHex(INT, sectorPerCluster); transmitByte(' ');
  reservedSectorCount = bpb->reservedSectorCount;
  rootCluster = bpb->rootCluster;// + (sector / sectorPerCluster) +1;
  firstDataSector = bpb->hiddenSectors + reservedSectorCount + (bpb->numberofFATs * bpb->FATsize_F32);

  dataSectors = bpb->totalSectors_F32
    - bpb->reservedSectorCount
    - ( bpb->numberofFATs * bpb->FATsize_F32);
  totalClusters = dataSectors / sectorPerCluster;
  //transmitHex(LONG, totalClusters); transmitByte(' ');

  if((getSetFreeCluster (FAT32_TOTAL_FREE, FAT32_GET, 0)) > totalClusters)  //check if FSinfo free clusters count is valid
    freeClusterCountUpdated = 0;
  else
    freeClusterCountUpdated = 1;
  return 0;
}

//***************************************************************************
//Function: to calculate first sector address of any given cluster
//Arguments: cluster number for which first sector is to be found
//return: first sector address
//***************************************************************************
unsigned long getFirstSector(unsigned long clusterNumber)
{
  return (((clusterNumber - 2) * sectorPerCluster) + firstDataSector);
}

//***************************************************************************
//Function: get cluster entry value from FAT to find out the next cluster in the chain
//or set new cluster entry in FAT
//Arguments: 1. current cluster number, 2. get_set (=FAT32_GET, if next cluster is to be found or = FAT32_SET,
//if next cluster is to be set 3. next cluster number, if argument#2 = FAT32_SET, else 0
//return: next cluster number, if if argument#2 = FAT32_GET, else 0
//****************************************************************************
unsigned long getSetNextCluster (unsigned long clusterNumber,
                                 unsigned char get_set,
                                 unsigned long clusterEntry)
{
  unsigned int FATEntryOffset;
  unsigned long *FATEntryValue;
  unsigned long FATEntrySector;
  unsigned char retry = 0;

  //get sector number of the cluster entry in the FAT
  FATEntrySector = unusedSectors + reservedSectorCount + ((clusterNumber * 4) / bytesPerSector) ;

  //get the offset address in that sector number
  FATEntryOffset = (unsigned int) ((clusterNumber * 4) % bytesPerSector);
  
  //read the sector into a buffer
  while(retry <10)
  { 
    if(!SD_readSingleBlock(FATEntrySector)) 
      break; 
    retry++;
  }

  //get the cluster address from the buffer
  FATEntryValue = (unsigned long *) &buffer[FATEntryOffset];
  // Obs.- check FATEntryValue
  if(get_set == FAT32_GET)
  {
//      printf("\n%ul",(*FATEntryValue) & 0x0fffffff); //testing
      return ((*FATEntryValue) & 0x0fffffff);
  }


  *FATEntryValue = clusterEntry;   //for setting new value in cluster entry in FAT

  SD_writeSingleBlock(FATEntrySector);

  return (0);
}

//********************************************************************************************
//Function: to get or set next free cluster or total free clusters in FSinfo sector of SD card
//Arguments: 1.flag:FAT32_TOTAL_FREE or FAT32_NEXT_FREE, 
//			 2.flag: FAT32_GET or FAT32_SET 
//			 3.new FS entry, when argument2 is FAT32_SET; or 0, when argument2 is FAT32_GET
//return: next free cluster, if arg1 is FAT32_NEXT_FREE & arg2 is FAT32_GET
//        total number of free clusters, if arg1 is FAT32_TOTAL_FREE & arg2 is FAT32_GET
//		  0xffffffff, if any error or if arg2 is FAT32_SET
//********************************************************************************************
unsigned long getSetFreeCluster(unsigned char totOrNext, unsigned char get_set, unsigned long FSEntry)
{
  struct FSInfo_Structure *FS = (struct FSInfo_Structure *) &buffer; // 512 bytes pointer
  unsigned char error;

  SD_readSingleBlock(unusedSectors + 1);

  if((FS->leadSignature != 0x41615252) || (FS->structureSignature != 0x61417272) || (FS->trailSignature !=0xaa550000))
    return 0xffffffff;

  if(get_set == FAT32_GET)
    {
      if(totOrNext == FAT32_TOTAL_FREE)
	return(FS->freeClusterCount);
      else // when totOrNext = FAT32_NEXT_FREE
	return(FS->nextFreeCluster);
    }
  else
    {
      if(totOrNext == FAT32_TOTAL_FREE)
	FS->freeClusterCount = FSEntry;
      else // when totOrNext = FAT32_NEXT_FREE
	FS->nextFreeCluster = FSEntry;
 
      error = SD_writeSingleBlock(unusedSectors + 1);	//update FSinfo
    }
  return 0xffffffff;
}

//***************************************************************************
//Function: to get DIR/FILE list or a single file address (cluster number) or to delete a specified file
//Arguments: #1 - flag: FAT32_GET_LIST, FAT32_GET_FILE or FAT32_DELETE #2 - pointer to file name (0 if arg#1 is FAT32_GET_LIST)
//return: first cluster of the file, if flag = FAT32_GET_FILE
//        print file/dir list of the root directory, if flag = FAT32_GET_LIST
//		  Delete the file mentioned in arg#2, if flag = FAT32_DELETE
//****************************************************************************

struct dir_Structure* findFiles (unsigned char flag, unsigned char *fileName)
{
  unsigned long cluster, sector, firstSector, firstCluster, nextCluster;
  struct dir_Structure *dir;
  unsigned int i;
  unsigned char j;

  cluster = rootCluster; //root cluster

    //controls loop TODO: review this
  while(1)
  {
    firstSector = getFirstSector (cluster);

    for(sector = 0; sector < sectorPerCluster; sector++)
    {
      SD_readSingleBlock (firstSector + sector);
      // buffer is filled with a sector content (512 bytes)
      
      for(i=0; i<bytesPerSector; i+=32)
      {
	dir = (struct dir_Structure *) &buffer[i];
	
	if(dir->name[0] == FAT32_EMPTY) //indicates end of the file list of the directory
	  {
	      
#ifdef VERBOSE_FAT32
	    if((flag == FAT32_GET_FILE) || (flag == FAT32_DELETE))
	      printf_P(PSTR("\nFile does not exist!"));
#endif
		
	    return 0;   
	  }
	if((dir->name[0] != FAT32_DELETED) && (dir->attrib != ATTR_LONG_NAME))
	  {
	    if((flag == FAT32_GET_FILE) || (flag == FAT32_DELETE))
	      {
		for(j=0; j<11; j++)
		  if(dir->name[j] != fileName[j]) break;
		// NOTE: j == 11 means FileName string of 11 size 0-10 characters, 11 = '\0'
		if(j == 11) // file found
		  {
		    if(flag == FAT32_GET_FILE)
		      {
			appendFileSector = firstSector + sector;
			appendFileLocation = i;
			appendStartCluster = (((unsigned long) dir->firstClusterHI) << 16) | dir->firstClusterLO;
			fileSize = dir->fileSize;
			return (dir);
		      }	
		    else    //when flag = FAT32_DELETE
		      {

#ifdef VERBOSE_FAT32
			printf_P(PSTR("\nDeleting..\n"));
#endif
		    
			firstCluster = (((unsigned long) dir->firstClusterHI) << 16) | dir->firstClusterLO;
                
			//mark file as 'deleted' in FAT table
			dir->name[0] = FAT32_DELETED;    
			SD_writeSingleBlock (firstSector+sector);
				 			 
			freeMemoryUpdate (FAT32_ADD, dir->fileSize);

			//update next free cluster entry in FSinfo sector
			cluster = getSetFreeCluster (FAT32_NEXT_FREE, FAT32_GET, 0);
			if(firstCluster < cluster)
			  getSetFreeCluster (FAT32_NEXT_FREE, FAT32_SET, firstCluster);

			//mark all the clusters allocated to the file as 'free'
			while(1)  
			  {
			    nextCluster = getSetNextCluster (firstCluster, FAT32_GET, 0);
			    getSetNextCluster (firstCluster, FAT32_SET, 0);
			    if(nextCluster > 0x0ffffff6) 
			      {
#ifdef VERBOSE_FAT32
				printf_P(PSTR("File Deleted!"));
#endif
				return 0;
			      }
			    firstCluster = nextCluster;
			  } 
		      }
		  }
	      }
	    else  //when flag = FAT32_GET_LIST
	      {
#ifdef VERBOSE_FAT32
		printf_P(PSTR("\n"));
#endif
		// REVIEW: prints fileName?
		for(j=0; j<11; j++)
		  {
		    if(j == 8) usart_tx(' ');
		    usart_tx(dir->name[j]);
		  }
#ifdef VERBOSE_FAT32
		printf_P(PSTR("   "));
#endif
		if((dir->attrib != 0x10) && (dir->attrib != 0x08))
		  {
#ifdef VERBOSE_FAT32
		    printf_P(PSTR("FILE"));
		    printf_P(PSTR("   "));
#endif
		    displayMemory (FAT32_LOW, dir->fileSize);
		  }
		else
		  {
#ifdef VERBOSE_FAT32
		    dir->attrib == 0x10 ? printf_P(PSTR("DIR")):printf_P(PSTR("ROOT"));
#endif
		  }

	      }
	  }
      }
    }

    cluster = (getSetNextCluster (cluster, FAT32_GET, 0));

    if(cluster > 0x0ffffff6)
      return 0;
    if(cluster == 0) 
    {
#ifdef VERBOSE_FAT32
      printf_P(PSTR("ff Error in getting cluster")); 
#endif
      return 0;
    }
  }
  return 0;
}

//***************************************************************************
//Function: if flag=FAT32_READ then to read file from SD card and send contents to UART 
//if flag=FAT32_VERIFY then functions will verify whether a specified file is already existing
//Arguments: flag (FAT32_READ or FAT32_VERIFY) and pointer to the file name
//return: 0, if normal operation or flag is FAT32_READ
//	      1, if file is already existing and flag = FAT32_VERIFY
//		  2, if file name is incompatible
//NOTE: Modified to show the last 200 bytes of the file

//***************************************************************************
unsigned char readFile (unsigned char flag, unsigned char *fileName)
{
  struct dir_Structure *dir;
  unsigned long cluster, byteCounter = 0, fileSize, firstSector;
  unsigned int k;
  unsigned char j, error;

  /* testing omiting this function*/
  //error = convertFileName (fileName); //convert fileName into FAT format
  //if(error) return 2;

  dir = findFiles (FAT32_GET_FILE, fileName); //get the file location
  if(dir == 0) 
    return (0);

  if(flag == FAT32_VERIFY) return (1);	//specified file name is already existing
  
  //  -- Reading a file --
  cluster = (((unsigned long) dir->firstClusterHI) << 16) | dir->firstClusterLO;

  fileSize = dir->fileSize;

#ifdef VERBOSE_FAT32
  printf_P(PSTR("\n\n"));
#endif

  while(1)
    {
      firstSector = getFirstSector (cluster);

      for(j=0; j<sectorPerCluster; j++)
	{
	  SD_readSingleBlock(firstSector + j);
	  
	  for(k=0; k<512; k++)
	    {
	      if(fileSize<200)
		usart_tx(buffer[k]);
	      else if((byteCounter) >= fileSize-200) // show only last 200 bytes 
		usart_tx(buffer[k]);
	      if ((byteCounter++) >= fileSize ) 
		return 0;
	    }
	  
	  /* Original was:
    
	  for(k=0; k<512; k++)
	    {
	      usart_tx(buffer[k]);
	      if ((byteCounter++) >= fileSize ) 
		return 0;
	    }
	  */
	}
      cluster = getSetNextCluster (cluster, FAT32_GET, 0);
      if(cluster == 0)
      {
#ifdef VERBOSE_FAT32
	printf_P(PSTR("rf Error in getting cluster"));
#endif
	return 0;
      }
    }
  return 0;
}

//***************************************************************************
//Function: to convert normal short file name into FAT format
//Arguments: pointer to the file name
//return: 0, if successful else 1.
//***************************************************************************
unsigned char convertFileName (unsigned char *fileName)
{
  unsigned char fileNameFAT[11]; //TODO: review this (only 11 chars?)
  unsigned char j, k;

  //for(j=0; j<11; j++) // TODO: review this
  for(j=0; j<12; j++) // TODO: review this
    if(fileName[j] == '.') break;

  if(j>8) // TODO: review this
    {
      //      printf("Invalid Filename %d %s", j, fileName);  
#ifdef VERBOSE_FAT32
      printf_P(PSTR("Invalid fileName.."));
      printf("%s",fileName);
#endif
      return 1;
    }

  for(k=0; k<j; k++) //setting file name
    fileNameFAT[k] = fileName[k];

  for(k=j; k<=7; k++) //filling file name trail with blanks
    fileNameFAT[k] = ' ';

  j++;
  for(k=8; k<11; k++) //setting file extention
  {
    if(fileName[j] != '\0')
      fileNameFAT[k] = fileName[j++];
    else //filling extension trail with blanks
      while(k<11)
	fileNameFAT[k++] = ' ';
  }
  fileNameFAT[10] = '\0'; // mark end of string

  /* for(j=0; j<11; j++) //converting small letters to caps */
  /*   if((fileNameFAT[j] >= 0x61) && (fileNameFAT[j] <= 0x7a)) */
  /*     fileNameFAT[j] -= 0x20; */

  for(j=0; j<11; j++)
    fileName[j] = fileNameFAT[j];

#ifdef VERBOSE_FAT32
    printf(">%s",fileName);
#endif

  return 0;


  // filling file name trail with blanks
/*   for(k=0 ; k<11 ; k++) */
/*       fileNameFAT[k]=' '; */

/*   for(j=0; j<11; j++) */
/*     { */
/*       // TODO: review this */
/*       // restricts names from chars SOH to + (1 to 44) */
/*       if(fileName[j] == '\0') // end of string */
/* 	break; */
/*       else if(fileName[j]<0x2D) */
/* 	{ */
/* #ifdef VERBOSE_FAT32 */
/* 	  printf_P(PSTR("Invalid Filename..")); */
/* 	  printf("%s",fileName); */
/* #endif */
/* 	  return 1; */
/* 	} */
/*       else // copy the fileName */
/* 	fileNameFAT[j] = fileName[j]; */
/*     } */

/*   fileNameFAT[10] = '\0'; // end of string */
/*   //fileNameFAT[11] = '\0'; // end of string (not work) */

/*   for(k=0 ; k<11 ; k++) // copy the valid file name */
/*       fileName[k] = fileNameFAT[k]; */

/* #ifdef VERBOSE_FAT32 */
/*   printf(">%s",fileName); */
/* #endif */
  
/*   return 0; */

}

//************************************************************************************
//Function: to create a file in FAT32 format in the root directory if given 
//			file name does not exist; if the file already exists then append the data
//Arguments: pointer to the file name, pointer to the new content to be added
//return: none
//************************************************************************************
void writeFile (unsigned char *fileName, char* new_content)
{
  unsigned char j, data, error, fileCreatedFlag = 0, start = 0, appendFile = 0, sectorEndFlag = 0, sector;
  unsigned int i, firstClusterHigh, firstClusterLow;
  struct dir_Structure *dir;
  unsigned long cluster, nextCluster, prevCluster, firstSector, clusterCount, extraMemory;

  j = readFile (FAT32_VERIFY, fileName);

  if(j == 1) 
    {
#ifdef VERBOSE_FAT32
      printf_P(PSTR("\n File already existing, appending data\n")); 
#endif

      // Note.- When using std libs be carefull redifining macros
      appendFile = 1;
      cluster = appendStartCluster;
      clusterCount=0;
      while(1) // TODO: control loop
	{
	  nextCluster = getSetNextCluster (cluster, FAT32_GET, 0);
	  if(nextCluster == EOF_FAT32)
	    break;
	  cluster = nextCluster;
	  clusterCount++;
	}

      sector = (fileSize - (clusterCount * sectorPerCluster * bytesPerSector)) / bytesPerSector; //last sector number of the last cluster of the file
      start = 1;
      //  appendFile();
      //  return;
    }
  else if(j == 2) 
    return; //invalid file name
  else
    {
#ifdef VERBOSE_FAT32
      printf_P(PSTR("\n Creating File.."));
#endif

      cluster = getSetFreeCluster (FAT32_NEXT_FREE, FAT32_GET, 0);
      if(cluster > totalClusters)
	cluster = rootCluster;

      cluster = searchNextFreeCluster(cluster);
      if(cluster == 0)
	{
#ifdef VERBOSE_FAT32
	  printf_P(PSTR("\n No free cluster!"));
#endif
	  return;
	}
      getSetNextCluster(cluster, FAT32_SET, EOF_FAT32);   //last cluster of the file, marked EOF_FAT32
   
      firstClusterHigh = (unsigned int) ((cluster & 0xffff0000) >> 16 );
      firstClusterLow = (unsigned int) ( cluster & 0x0000ffff);
      fileSize = 0;
    }


  // Filling the buffer[] using *new_content
  while(1) // TODO: control loop
    {
      if(start)
	{
	  start = 0;
	  startBlock = getFirstSector (cluster) + sector;
	  SD_readSingleBlock (startBlock);
	  i = fileSize % bytesPerSector;
	  j = sector;
	}
      else
	{
	  startBlock = getFirstSector (cluster);
	  i=0;
	  j=0;
	}

#ifdef VERBOSE_FAT32
      printf_P(PSTR("New content to be added:\n"));
#endif

      data = '\0';

      do
	{
	  if(sectorEndFlag == 1) //special case when the last character in previous sector was '\r'
	    {
#ifdef VERBOSE_FAT32
	      usart_tx('\n');
#endif
	      buffer[i++] = '\n'; //appending 'Line Feed (LF)' character
	      fileSize++;
	    }

	  sectorEndFlag = 0;
	
	  //	data = usart_rx();  // *!)changed here to write files from a given string
	  data = *new_content;
	  new_content++;
	  /* 	if(data == 0x08)	//'Back Space' key */
	  /* 	  {  */
	  /* 	    if(i != 0) */
	  /* 	      {  */

	  /* #ifdef VERBOSE_FAT32 */
	  /* 		usart_tx(data); */
	  /* 		usart_tx(' ');  */
	  /* 		usart_tx(data);  */
	  /* #endif */
	  /* 		i--;  */
	  /* 		fileSize--; */
	  /* 	      }  */
	  /* 	    continue;      */
	  /* 	  } */
#ifdef VERBOSE_FAT32
	  usart_tx(data);  
#endif

	  buffer[i++] = data;
	  fileSize++;
	  if(data == '\r')  //'Carriege Return (CR)' character
	    {
	      if(i == 512)
		sectorEndFlag = 1;  //flag to indicate that the appended '\n' char should be put in the next sector
	      else
		{ 

#ifdef VERBOSE_FAT32
		  usart_tx ('\n');
#endif
		  buffer[i++] = '\n'; //appending 'Line Feed (LF)' character
		  fileSize++;
		}
	    }
	 
	  if(i >= 512)   //though 'i' will never become greater than 512, it's kept here to avoid 
	    {				//infinite loop in case it happens to be greater than 512 due to some data corruption
	      i=0;
	      error = SD_writeSingleBlock (startBlock);
	      j++;
	      if(j == sectorPerCluster)
		{
		  j = 0;
		  break;
		}
	      startBlock++; 
	    }
	} while (data != '\0');

      if(data == '\0') 
	{
	  fileSize--;	//to remove the last entered '\0' character
	  i--;
	  for(;i<512;i++)  //fill the rest of the buffer with 0x00
	    buffer[i]= 0x00;
	  error = SD_writeSingleBlock (startBlock);

	  break;
	} 
	  
      prevCluster = cluster;

      cluster = searchNextFreeCluster(prevCluster); //look for a free cluster starting from the current cluster

      if(cluster == 0)
	{
#ifdef VERBOSE_FAT32
	  printf_P(PSTR("\n No free cluster!"));
#endif
	  return;
	}

      getSetNextCluster(prevCluster, FAT32_SET, cluster);
      getSetNextCluster(cluster, FAT32_SET, EOF_FAT32);   //last cluster of the file, marked EOF_FAT32
    }

  getSetFreeCluster (FAT32_NEXT_FREE, FAT32_SET, cluster); //update FSinfo next free cluster entry

  if(appendFile)  //executes this loop if file is to be appended
    {
      SD_readSingleBlock (appendFileSector);    
      dir = (struct dir_Structure *) &buffer[appendFileLocation]; 
      extraMemory = fileSize - dir->fileSize;
      dir->fileSize = fileSize;
      SD_writeSingleBlock (appendFileSector);
      freeMemoryUpdate (FAT32_REMOVE, extraMemory); //updating free memory count in FSinfo sector;

#ifdef VERBOSE_FAT32
      printf_P(PSTR("\nFile appended!\n"));
#endif

      return;
    }

  //executes following portion when new file is created

  prevCluster = rootCluster; //root cluster

  while(1) // TODO:control loop
    {
      firstSector = getFirstSector (prevCluster);

      for(sector = 0; sector < sectorPerCluster; sector++)
	{
	  SD_readSingleBlock (firstSector + sector);
	
	  for(i=0; i<bytesPerSector; i+=32)
	    {
	      dir = (struct dir_Structure *) &buffer[i];

	      if(fileCreatedFlag)   //to mark last directory entry with 0x00 (empty) mark
		{ 					  //indicating end of the directory file list
		  dir->name[0] = 0x00;
		  return;
		}
	      
	      if((dir->name[0] == FAT32_EMPTY) || (dir->name[0] == FAT32_DELETED))  //looking for an empty slot to enter file info
		{
		  for(j=0; j<11; j++)
		    dir->name[j] = fileName[j];
		  dir->attrib = ATTR_ARCHIVE;	//settting file attribute as 'archive'
		  dir->NTreserved = 0;			//always set to 0
		  dir->timeTenth = 0;			//always set to 0
		  // TODO: add time creation to file
		  dir->createTime = 0x9684;		//fixed time of creation 
		  dir->createDate = 0x3a37;		//fixed date of creation
		  dir->lastAccessDate = 0x3a37;	//fixed date of last access
		  dir->writeTime = 0x9684;		//fixed time of last write
		  dir->writeDate = 0x3a37;		//fixed date of last write
		  dir->firstClusterHI = firstClusterHigh;
		  dir->firstClusterLO = firstClusterLow;
		  dir->fileSize = fileSize;

		  SD_writeSingleBlock (firstSector + sector);
		  fileCreatedFlag = 1;

#ifdef VERBOSE_FAT32 //WARNING: not using the macro makes memeory leak and reboot!
		  printf_P(PSTR("\nFile Created!\n"));  
#endif // VERBOSE_FAT32

		  freeMemoryUpdate (FAT32_REMOVE, fileSize); //updating free memory count in FSinfo sector
	     
		}
	    }
	}

      cluster = getSetNextCluster (prevCluster, FAT32_GET, 0);

      if(cluster > 0x0ffffff6)
	{
	  if(cluster == EOF_FAT32)   //this situation will come when total files in root is multiple of (32*sectorPerCluster)
	    {  
	      cluster = searchNextFreeCluster(prevCluster); //find next cluster for root directory entries
	      getSetNextCluster(prevCluster, FAT32_SET, cluster); //link the new cluster of root to the previous cluster
	      getSetNextCluster(cluster, FAT32_SET, EOF_FAT32);  //set the new cluster as end of the root directory
	    } 
	  else
	    {	

#ifdef VERBOSE_FAT32
	      printf_P(PSTR("End of Cluster chain"));
#endif // 

	      return;
	    }
	}
      if(cluster == 0) 
	{

#ifdef VERBOSE_FAT32
	  printf_P(PSTR("wf Error in getting cluster")); 
#endif

	  return;
	}
   
      prevCluster = cluster;
    }
 
  return;
}


//***************************************************************************
//Function: to search for the next free cluster in the root directory
//          starting from a specified cluster
//Arguments: Starting cluster
//return: the next free cluster
//****************************************************************
unsigned long searchNextFreeCluster (unsigned long startCluster)
{
  unsigned long cluster, *value, sector;
  unsigned char i;
    
  startCluster -=  (startCluster % 128);   //to start with the first file in a FAT sector
  for(cluster =startCluster; cluster <totalClusters; cluster+=128) 
    {
      sector = unusedSectors + reservedSectorCount + ((cluster * 4) / bytesPerSector);
      SD_readSingleBlock(sector);
      for(i=0; i<128; i++)
	{
	  value = (unsigned long *) &buffer[i*4];
	  if(((*value) & 0x0fffffff) == 0)
            return(cluster+i);
	}  
    } 

  return 0;
}

//***************************************************************************
//Function: to display total memory and free memory of SD card, using UART
//Arguments: none
//return: none
//Note: this routine can take upto 15sec for 1GB card (@1MHz clock)
//it tries to read from SD whether a free cluster count is stored, if it is stored
//then it will return immediately. Otherwise it will count the total number of
//free clusters, which takes time
//****************************************************************************
void memoryStatistics (void)
{
  unsigned long freeClusters, totalClusterCount, cluster;
  unsigned long totalMemory, freeMemory;
  unsigned long sector, *value;
  unsigned int i;


  totalMemory = totalClusters * sectorPerCluster / 1024;
  totalMemory *= bytesPerSector;

  printf_P(PSTR("\nTotal Memory: "));

  displayMemory (FAT32_HIGH, totalMemory);

  freeClusters = getSetFreeCluster (FAT32_TOTAL_FREE, FAT32_GET, 0);
  //freeClusters = 0xffffffff;    

  if(freeClusters > totalClusters)
    {
      freeClusterCountUpdated = 0;
      freeClusters = 0;
      totalClusterCount = 0;
      cluster = rootCluster;    
      while(1)
	{
	  sector = unusedSectors + reservedSectorCount + ((cluster * 4) / bytesPerSector) ;
	  SD_readSingleBlock(sector);
	  for(i=0; i<128; i++)
	    {
	      value = (unsigned long *) &buffer[i*4];
	      if(((*value)& 0x0fffffff) == 0)
		freeClusters++;;
        
	      totalClusterCount++;
	      if(totalClusterCount == (totalClusters+2)) break;
	    }  
	  if(i < 128) break;
	  cluster+=128;
	} 
    }

  if(!freeClusterCountUpdated)
    getSetFreeCluster (FAT32_TOTAL_FREE, FAT32_SET, freeClusters); //update FSinfo next free cluster entry
  freeClusterCountUpdated = 1;  //set flag
  freeMemory = freeClusters * sectorPerCluster / 1024;
  freeMemory *= bytesPerSector ;
  //printf_P(PSTR("\n Free Memory: "));
  printf_P(PSTR("\n Free Memory:"));
  displayMemory (FAT32_HIGH, freeMemory);
}

//************************************************************
//Function: To convert the unsigned long value of memory into 
//          text string and send to UART
//Arguments: 1. unsigned char flag. If flag is FAT32_HIGH, memory will be displayed in KBytes, else in Bytes. 
//			 2. unsigned long memory value
//return: none
//************************************************************
void displayMemory (unsigned char flag, unsigned long memory)
{
  unsigned char memoryString[] = "              Bytes"; //19 character long string for memory display
  unsigned char i;
  for(i=12; i>0; i--) //converting freeMemory into ASCII string
    {
      if(i==5 || i==9) 
	{
	  memoryString[i-1] = ',';  
	  i--;
	}
      memoryString[i-1] = (memory % 10) | 0x30;
      memory /= 10;
      if(memory == 0) break;
    }
  if(flag == FAT32_HIGH)  memoryString[13] = 'K';
  printf("%s",memoryString);  //check this function
}

//********************************************************************
//Function: to delete a specified file from the root directory
//Arguments: pointer to the file name
//return: none
//********************************************************************
void deleteFile (unsigned char *fileName)
{
  unsigned char error;

  error = convertFileName (fileName);
  if(error) return;

  findFiles (FAT32_DELETE, fileName);
}

//********************************************************************
//Function: update the free memory count in the FSinfo sector. 
//			Whenever a file is deleted or created, this function will be called
//			to FAT32_ADD or FAT32_REMOVE clusters occupied by the file
//Arguments: #1.flag FAT32_ADD or FAT32_REMOVE #2.file size in Bytes
//return: none
//********************************************************************
void freeMemoryUpdate (unsigned char flag, unsigned long size)
{
  unsigned long freeClusters;
  //convert file size into number of clusters occupied
  if((size % 512) == 0) size = size / 512;
  else size = (size / 512) +1;
  if((size % 8) == 0) size = size / 8;
  else size = (size / 8) +1;

  if(freeClusterCountUpdated)
    {
      freeClusters = getSetFreeCluster (FAT32_TOTAL_FREE, FAT32_GET, 0);
      if(flag == FAT32_ADD)
	freeClusters = freeClusters + size;
      else  //when flag = FAT32_REMOVE
	freeClusters = freeClusters - size;
      getSetFreeCluster (FAT32_TOTAL_FREE, FAT32_SET, freeClusters);
    }
}


// --------------------


//******** END ****** www.dharmanitech.com *****
